<template>
  <div>
    <el-empty
      v-if="!props.testName"
      description="先选择测试项"
      style="height: 600px; width: 100%"
    />
    <div v-else ref="chartRef" style="height: 600px; width: 100%"></div>
  </div>
</template>

<script setup lang="ts">
import {
  ref,
  onMounted,
  onBeforeUnmount,
  nextTick,
  watch,
  computed
} from "vue";
import axios from "axios";
import * as echarts from "echarts";
import type { ECharts, EChartsOption } from "echarts";
import { ElMessage } from "element-plus";

/**
 * Props 说明
 * apiUrl: 数据接口
 *   - mode="bins": 返回 {data:[{site,binCenter,count}...], mean, std, lsl, usl} 或直接数组
 *   - mode="raw" : 返回 {data:[{site,value}...]} 或直接数组（组件前端分箱）
 * testName: 测试项名
 * active:   所在 tab 是否可见（切到可见会重载与 resize）
 * binCount: raw 模式分箱数量（不设则用 FD 法）
 * stacked:  多 site 堆叠；false 为并排
 * globalBins: raw 模式是否所有 site 统一区间（便于比较）
 * showFit:  是否显示高斯拟合
 * showSpecs:是否显示 LSL/Mean/USL 竖线
 * mode:     "bins" | "raw"
 */
const props = withDefaults(
  defineProps<{
    apiUrl?: string;
    testName?: string;
    active?: boolean;
    mode?: "bins" | "raw";
    binCount?: number | null;
    stacked?: boolean;
    globalBins?: boolean;
    showFit?: boolean;
    showSpecs?: boolean;
    centerOn?: "mean" | "mid" | "value";
    centerValue?: number | null;
    enableZoom?: boolean; // 是否显示 dataZoom，默认关闭
    dbPath?: string;
  }>(),
  {
    apiUrl: "https://localhost:44389/api/Stdf/GetSiteHistogram",
    active: false,
    mode: "bins",
    binCount: null,
    stacked: true,
    globalBins: true,
    showFit: true,
    showSpecs: true,
    centerOn: "mean",
    centerValue: null,
    enableZoom: false,
    dbPath: null
  }
);

type RawPoint = { site: number; value: number };
type BinPoint = { site: number; binCenter: number; count: number };

const chartRef = ref<HTMLDivElement | null>(null);
let chart: ECharts | null = null;

const rawPoints = ref<RawPoint[]>([]);
const binPoints = ref<BinPoint[]>([]);

// 规格/均值（可由后端提供）
const meanRef = ref<number | null>(null);
const stdRef = ref<number | null>(null);
const lslRef = ref<number | null>(null);
const uslRef = ref<number | null>(null);

/* ========== 初始化 / 销毁 / 自适应 ========== */
function initChart() {
  if (!chartRef.value) return;
  chart = echarts.init(chartRef.value);
}
function disposeChart() {
  chart?.dispose();
  chart = null;
}
function resize() {
  chart?.resize();
}

/* ========== 工具：稳健分箱（FD 法） / 计数 / 容错 ========== */
function quantile(sorted: number[], q: number) {
  const n = sorted.length;
  if (!n) return NaN;
  const pos = (n - 1) * q,
    base = Math.floor(pos),
    rest = pos - base;
  return sorted[base + 1] !== undefined
    ? sorted[base] + rest * (sorted[base + 1] - sorted[base])
    : sorted[base];
}
function finiteNumbers(values: number[]) {
  return values.filter(v => typeof v === "number" && Number.isFinite(v));
}
function computeBins(values: number[], binCount?: number | null) {
  const v = finiteNumbers(values);
  if (v.length === 0) return { edges: [] as number[], centers: [] as number[] };
  const sorted = v.slice().sort((a, b) => a - b);
  const min = sorted[0],
    max = sorted[sorted.length - 1];
  if (min === max) {
    const eps = Math.max(Math.abs(min) * 1e-9, 1e-12);
    return { edges: [min - eps, min + eps], centers: [min] };
  }
  let k = binCount ?? null;
  if (!k || !Number.isFinite(k) || k <= 0) {
    const q1 = quantile(sorted, 0.25),
      q3 = quantile(sorted, 0.75);
    const iqr = Math.max(q3 - q1, Number.EPSILON);
    const h = (2 * iqr) / Math.cbrt(sorted.length);
    k = Math.ceil((max - min) / h);
    k = Math.min(80, Math.max(10, k));
  }
  if (!Number.isFinite(k) || k < 1) k = 1;
  const step = (max - min) / k;
  if (!Number.isFinite(step) || step <= 0)
    return { edges: [min, max], centers: [(min + max) / 2] };
  const edges = Array.from({ length: k + 1 }, (_, i) => min + i * step);
  const centers = Array.from(
    { length: k },
    (_, i) => (edges[i] + edges[i + 1]) / 2
  );
  return { edges, centers };
}
function histogramCounts(values: number[], edges: number[]) {
  const vals = finiteNumbers(values);
  const k = edges.length - 1;
  if (k < 1 || vals.length === 0) return [];
  const counts = Array(k).fill(0) as number[];
  const lastEdge = edges[k];
  for (const v of vals) {
    if (v < edges[0]) continue;
    if (v >= lastEdge) {
      counts[k - 1]++;
      continue;
    }
    // 二分定位
    let lo = 0,
      hi = k - 1,
      idx = 0;
    while (lo <= hi) {
      const mid = (lo + hi) >> 1;
      if (v >= edges[mid + 1]) lo = mid + 1;
      else if (v < edges[mid]) hi = mid - 1;
      else {
        idx = mid;
        break;
      }
    }
    counts[idx]++;
  }
  return counts;
}

function centeredRange(
  minData: number | undefined,
  maxData: number | undefined,
  center: number | null
) {
  if (
    !Number.isFinite(minData as number) ||
    !Number.isFinite(maxData as number)
  ) {
    return { min: minData, max: maxData };
  }
  if (center == null || !Number.isFinite(center)) {
    return { min: minData, max: maxData };
  }
  const min = minData as number,
    max = maxData as number;
  const half = Math.max(center - min, max - center);
  return { min: center - half, max: center + half };
}

/* ========== 高斯拟合曲线（根据 bins 构造） ========== */
function buildGaussianLineFromBins(
  arr: BinPoint[],
  mean: number | null,
  std: number | null
) {
  if (
    !arr.length ||
    mean == null ||
    std == null ||
    !Number.isFinite(mean) ||
    !Number.isFinite(std) ||
    std <= 0
  )
    return [];
  const xs = Array.from(new Set(arr.map(d => d.binCenter))).sort(
    (a, b) => a - b
  );
  const maxCount = Math.max(...arr.map(d => d.count));
  const step = xs.length > 1 ? xs[1] - xs[0] : 1;
  // 缩放：使曲线高度与直方图数量同量纲（面积近似）
  const scale = maxCount * 1.0;
  const m = mean,
    s = std;
  return xs.map(x => {
    const y =
      Math.exp(-0.5 * ((x - m) / s) ** 2) / (s * Math.sqrt(2 * Math.PI));
    return [x, y * scale * step];
  });
}

/* ========== 拉取数据 ========== */
async function load() {
  if (!props.testName) return;
  if (!chart) initChart();
  if (!chart) {
    ElMessage.info("请先初始化图表");
    return;
  }

  chart.showLoading({ text: "Loading..." });
  try {
    const { data } = await axios.get(props.apiUrl!, {
      params: { testName: props.testName, dbPath: props.dbPath }
    });

    if (props.mode === "bins") {
      const arr = Array.isArray(data) ? data : (data?.data ?? []);
      binPoints.value = arr
        .map((it: any) =>
          Array.isArray(it)
            ? { site: +it[0], binCenter: +it[1], count: +it[2] }
            : { site: +it.site, binCenter: +it.binCenter, count: +it.count }
        )
        .filter(
          d =>
            Number.isFinite(d.site) &&
            Number.isFinite(d.binCenter) &&
            Number.isFinite(d.count) &&
            d.count >= 0
        );

      // 后端统计
      const m = +((data?.mean ?? data?.Mean) as any);
      const s = +((data?.std ?? data?.Std) as any);
      const l = +((data?.lsl ?? data?.LSL) as any);
      const u = +((data?.usl ?? data?.USL) as any);
      meanRef.value = Number.isFinite(m) ? m : null;
      stdRef.value = Number.isFinite(s) ? s : null;
      lslRef.value = Number.isFinite(l) ? l : null;
      uslRef.value = Number.isFinite(u) ? u : null;
    } else {
      // raw：原始点；前端分箱
      const arr = Array.isArray(data) ? data : (data?.data ?? []);
      rawPoints.value = arr
        .map((it: any) =>
          Array.isArray(it)
            ? { site: +it[0], value: +it[1] }
            : { site: +it.site, value: +it.value }
        )
        .filter(d => Number.isFinite(d.site) && Number.isFinite(d.value));

      // 同时在前端计算 mean/std（用于拟合/均值线；也可不算）
      const vals = rawPoints.value.map(d => d.value);
      if (vals.length) {
        const finite = finiteNumbers(vals);
        const mean = finite.reduce((a, b) => a + b, 0) / finite.length;
        let std = 0;
        if (finite.length > 1) {
          const ss = finite.reduce((s, v) => s + (v - mean) * (v - mean), 0);
          std = Math.sqrt(ss / (finite.length - 1));
        }
        meanRef.value = Number.isFinite(mean) ? mean : null;
        stdRef.value = Number.isFinite(std) ? std : null;
      } else {
        meanRef.value = stdRef.value = null;
      }
      // 规格线可留空或另设接口获取
      lslRef.value = data?.lsl ?? data?.LSL ?? null;
      uslRef.value = data?.usl ?? data?.USL ?? null;
      if (!Number.isFinite(+lslRef.value!)) lslRef.value = null;
      if (!Number.isFinite(+uslRef.value!)) uslRef.value = null;
    }

    chart.setOption(buildOption(), true);
  } catch (err: any) {
    console.error(err);
    ElMessage.error(`加载失败：${err?.message || err}`);
  } finally {
    chart?.hideLoading();
  }
}

/* ========== 组织系列与坐标 ========== */
// const seriesAndAxis = computed(() => {
//   if (props.mode === "bins") {
//     const arr = binPoints.value;
//     const sites = Array.from(new Set(arr.map(d => d.site))).sort(
//       (a, b) => a - b
//     );
//     // const series: any[] = sites.map(site => ({
//     //   name: `S${String(site).padStart(2, "0")}`,
//     //   type: "bar",
//     //   stack: props.stacked ? "hist" : undefined,
//     //   large: true,
//     //   itemStyle: { opacity: 0.6 },
//     //   emphasis: { focus: "series" },
//     //   data: arr.filter(d => d.site === site).map(d => [d.binCenter, d.count])
//     // }));
//     const series: any[] = sites.map(site => ({
//       name: `S${String(site).padStart(2, "0")}`,
//       type: "bar",
//       // stack: props.stacked ? "hist" : undefined,   // ❌ 先注释掉
//       barGap: "30%", // ← 每个 site 并排显示
//       barCategoryGap: "20%", // ← 增加柱间距
//       large: true,
//       itemStyle: { opacity: 0.6 },
//       data: arr.filter(d => d.site === site).map(d => [d.binCenter, d.count])
//     }));

//     // 全局拟合曲线（覆盖所有 site 的总分布）
//     if (props.showFit) {
//       const gauss = buildGaussianLineFromBins(arr, meanRef.value, stdRef.value);
//       if (gauss.length) {
//         series.push({
//           name: "Gaussian Fit",
//           type: "line",
//           smooth: true,
//           showSymbol: false,
//           lineStyle: { width: 2 },
//           yAxisIndex: 0,
//           data: gauss
//         });
//       }
//     }
//     return {
//       series,
//       xMin: undefined as number | undefined,
//       xMax: undefined as number | undefined
//     };
//   }

//   // raw：分组并分箱
//   const arr = rawPoints.value;
//   const bySite = new Map<number, number[]>();
//   for (const { site, value } of arr) {
//     if (!Number.isFinite(value)) continue;
//     if (!bySite.has(site)) bySite.set(site, []);
//     bySite.get(site)!.push(value);
//   }

//   let edges: number[] = [],
//     centers: number[] = [];
//   if (props.globalBins && arr.length) {
//     const allVals = arr.map(d => d.value).filter(v => Number.isFinite(v));
//     const bins = computeBins(allVals, props.binCount ?? null);
//     edges = bins.edges;
//     centers = bins.centers;
//     if (edges.length < 2 || centers.length < 1) {
//       return {
//         series: [] as any[],
//         xMin: undefined as number | undefined,
//         xMax: undefined as number | undefined
//       };
//     }
//   }

//   const series = Array.from(bySite.entries())
//     .sort((a, b) => a[0] - b[0])
//     .map(([site, values]) => {
//       const vals = values.filter(v => Number.isFinite(v));
//       if (!vals.length) return null;
//       let e = edges,
//         c = centers;
//       if (!props.globalBins) {
//         const bins = computeBins(vals, props.binCount ?? null);
//         e = bins.edges;
//         c = bins.centers;
//         if (e.length < 2 || c.length < 1) return null;
//       }
//       const counts = histogramCounts(vals, e);
//       if (!counts.length) return null;
//       const data = c.map((x, i) => [x, counts[i]]);
//       return {
//         name: `S${String(site).padStart(2, "0")}`,
//         type: "bar",
//         stack: props.stacked ? "hist" : undefined,
//         large: true,
//         itemStyle: { opacity: 0.6 },
//         data
//       };
//     })
//     .filter(Boolean) as any[];

//   // 原始模式的拟合曲线（对全部点）
//   if (
//     props.showFit &&
//     meanRef.value != null &&
//     stdRef.value != null &&
//     props.globalBins &&
//     centers.length
//   ) {
//     // 以全局 centers 生成曲线
//     const maxCount = Math.max(
//       ...series.flatMap(s => s.data.map((d: any) => d[1]))
//     );
//     const step = centers.length > 1 ? centers[1] - centers[0] : 1;
//     const scale = maxCount * 1.0;
//     const m = meanRef.value!,
//       s = stdRef.value!;
//     const gauss = centers.map(x => {
//       const y =
//         Math.exp(-0.5 * ((x - m) / s) ** 2) / (s * Math.sqrt(2 * Math.PI));
//       return [x, y * scale * step];
//     });
//     series.push({
//       name: "Gaussian Fit",
//       type: "line",
//       smooth: true,
//       showSymbol: false,
//       lineStyle: { width: 2 },
//       data: gauss
//     });
//   }

//   const xMin = props.globalBins && edges.length ? edges[0] : undefined;
//   const xMax =
//     props.globalBins && edges.length ? edges[edges.length - 1] : undefined;
//   return { series, xMin, xMax };
// });
const seriesAndAxis = computed(() => {
  if (props.mode === "bins") {
    const arr = binPoints.value;
    const sites = Array.from(new Set(arr.map(d => d.site))).sort(
      (a, b) => a - b
    );

    const xs = arr.map(d => d.binCenter).filter(Number.isFinite);
    const minData = xs.length ? Math.min(...xs) : undefined;
    const maxData = xs.length ? Math.max(...xs) : undefined;

    // const series: any[] = sites.map(site => ({
    //   name: `S${String(site).padStart(2, "0")}`,
    //   type: "bar",
    //   // 如果 bins 是统一的，可用 stack；否则建议并排
    //   stack: props.stacked ? "hist" : undefined,
    //   large: true,
    //   itemStyle: { opacity: 0.6 },
    //   data: arr.filter(d => d.site === site).map(d => [d.binCenter, d.count])
    // }));
    const series: any[] = sites.map(site => ({
      name: `S${String(site).padStart(2, "0")}`,
      type: "bar",
      // stack: props.stacked ? "hist" : undefined,   // ❌ 先注释掉
      barGap: "30%", // ← 每个 site 并排显示
      barCategoryGap: "20%", // ← 增加柱间距
      large: true,
      itemStyle: { opacity: 0.6 },
      data: arr.filter(d => d.site === site).map(d => [d.binCenter, d.count])
    }));
    if (props.showFit) {
      const gauss = buildGaussianLineFromBins(arr, meanRef.value, stdRef.value);
      if (gauss.length)
        series.push({
          name: "Gaussian Fit",
          type: "line",
          smooth: true,
          showSymbol: false,
          lineStyle: { width: 2 },
          data: gauss
        });
    }
    return { series, minData, maxData };
  }

  // raw
  const arr = rawPoints.value;
  const valuesAll = arr.map(d => d.value).filter(v => Number.isFinite(v));
  let minData: number | undefined, maxData: number | undefined;

  let edges: number[] = [],
    centers: number[] = [];
  if (props.globalBins && valuesAll.length) {
    const bins = computeBins(valuesAll, props.binCount ?? null);
    edges = bins.edges;
    centers = bins.centers;
    if (edges.length >= 2) {
      minData = edges[0];
      maxData = edges[edges.length - 1];
    }
  } else if (valuesAll.length) {
    minData = Math.min(...valuesAll);
    maxData = Math.max(...valuesAll);
  }

  const bySite = new Map<number, number[]>();
  for (const { site, value } of arr) {
    if (!Number.isFinite(value)) continue;
    (bySite.get(site) ?? bySite.set(site, []).get(site)!)!.push(value);
  }

  const series: any[] = Array.from(bySite.entries())
    .sort((a, b) => a[0] - b[0])
    .map(([site, values]) => {
      const vals = values.filter(Number.isFinite);
      if (!vals.length) return null;
      let e = edges,
        c = centers;
      if (!props.globalBins) {
        const bins = computeBins(vals, props.binCount ?? null);
        e = bins.edges;
        c = bins.centers;
        if (e.length < 2 || c.length < 1) return null;
      }
      const counts = histogramCounts(vals, e);
      const data = c.map((x, i) => [x, counts[i]]);
      return {
        name: `S${String(site).padStart(2, "0")}`,
        type: "bar",
        stack: props.stacked ? "hist" : undefined,
        large: true,
        itemStyle: { opacity: 0.6 },
        data
      };
    })
    .filter(Boolean) as any[];

  if (
    props.showFit &&
    meanRef.value != null &&
    stdRef.value != null &&
    props.globalBins &&
    centers.length
  ) {
    const maxCount = Math.max(
      ...series.flatMap(s => s.data.map((d: any) => d[1]))
    );
    const step = centers.length > 1 ? centers[1] - centers[0] : 1;
    const scale = maxCount * 1.0;
    const m = meanRef.value!,
      s = stdRef.value!;
    const gauss = centers.map(x => {
      const y =
        Math.exp(-0.5 * ((x - m) / s) ** 2) / (s * Math.sqrt(2 * Math.PI));
      return [x, y * scale * step];
    });
    series.push({
      name: "Gaussian Fit",
      type: "line",
      smooth: true,
      showSymbol: false,
      lineStyle: { width: 2 },
      data: gauss
    });
  }

  return { series, minData, maxData };
});
/* ========== 规格/均值竖线 ========== */
function buildSpecMarkLine() {
  if (!props.showSpecs) return null;
  const data: any[] = [];
  if (lslRef.value != null)
    data.push({ xAxis: lslRef.value, name: String(lslRef.value.toFixed(4)) });
  if (meanRef.value != null)
    data.push({
      xAxis: meanRef.value,
      name: String(meanRef.value.toFixed(4)),
      lineStyle: { type: "dashed" }
    });
  if (uslRef.value != null)
    data.push({ xAxis: uslRef.value, name: String(uslRef.value.toFixed(4)) });
  if (!data.length) return null;
  return {
    symbol: "none",
    label: {
      show: true,
      formatter: "{b}",
      position: "start" as const,
      color: "#666"
    },
    lineStyle: { width: 1.5, color: "#c00" },
    data
  };
}

/* ========== 构建 Option ========== */
// function buildOption(): EChartsOption {
//   const { series, xMin, xMax } = seriesAndAxis.value;
//   const option: EChartsOption = {
//     title: {
//       text: `当前测试项 - ${props.testName}`,
//       left: "center",
//       top: 10,
//       textStyle: { fontSize: 16, fontWeight: "bold" }
//     },
//     animation: false,
//     grid: { left: "8%", right: "5%", top: "12%", bottom: 80 },
//     legend: { top: 40, type: "scroll" },
//     toolbox: {
//       right: 10,
//       feature: { dataZoom: {}, restore: {}, saveAsImage: {} }
//     },
//     tooltip: {
//       trigger: "axis",
//       axisPointer: { type: "shadow" },
//       formatter: (items: any[]) => {
//         if (!items?.length) return "";
//         const x = items[0].value[0];
//         const header = `值: <b>${typeof x === "number" ? x.toFixed(6) : x}</b>`;
//         const lines = items.map(
//           it =>
//             `<div><span style="display:inline-block;margin-right:6px;width:10px;height:10px;background:${it.color}"></span>
//            ${it.seriesName}: <b>${it.value[1]}</b></div>`
//         );
//         return `<div>${header}</div>${lines.join("")}`;
//       }
//     },
//     dataZoom: [
//       { type: "inside", xAxisIndex: 0 },
//       { type: "slider", xAxisIndex: 0, height: 50 }
//     ],
//     xAxis: {
//       type: "value",
//       name: "Test Value",
//       min: xMin,
//       max: xMax,
//       axisLabel: { rotate: 35 }
//     },
//     yAxis: { type: "value", name: "Count", minInterval: 1 },
//     series: [...series]
//   };

//   // 独立的“规格/均值竖线”辅助 series，避免受图例开关影响
//   const specLines = buildSpecMarkLine();
//   if (specLines) {
//     (option.series as any[]).push({
//       name: "spec-lines",
//       type: "line",
//       data: [],
//       markLine: specLines,
//       tooltip: { show: false },
//       legendHoverLink: false
//     });
//   }

//   return option;
// }
function buildOption(): EChartsOption {
  const { series, minData, maxData } = seriesAndAxis.value;

  // 选择中心值
  let center: number | null = null;
  if (props.centerOn === "mean") center = meanRef.value ?? null;
  else if (props.centerOn === "value") center = props.centerValue ?? null;
  else if (
    props.centerOn === "mid" &&
    Number.isFinite(minData!) &&
    Number.isFinite(maxData!)
  ) {
    center = ((minData as number) + (maxData as number)) / 2;
  }

  // 计算对称范围
  const { min, max } = centeredRange(minData, maxData, center);

  const option: EChartsOption = {
    title: {
      text: `当前测试项 - ${props.testName}`,
      left: "center",
      top: 10,
      textStyle: { fontSize: 16, fontWeight: "bold" }
    },
    animation: false,
    grid: { left: "8%", right: "5%", top: "12%", bottom: 80 },
    legend: { top: 40, type: "scroll" },
    toolbox: {
      right: 10,
      feature: { dataZoom: {}, restore: {}, saveAsImage: {} }
    },

    // 如果你完全不想显示/使用缩放，把 dataZoom 整段去掉，或用 props.enableZoom 控制
    dataZoom: props.enableZoom
      ? [
          { type: "inside", xAxisIndex: 0 },
          { type: "slider", xAxisIndex: 0, height: 50 },
          { type: "inside", yAxisIndex: 0 },
          { type: "slider", yAxisIndex: 0, width: 12, right: 6 }
        ]
      : [],

    xAxis: {
      type: "value",
      name: "Test Value",
      min, // ← 以中心对称
      max,
      axisLabel: { rotate: 35 }
    },
    yAxis: { type: "value", name: "Count", minInterval: 1 },
    series: [...series]
  };

  // 规格/均值竖线（如果需要）
  const specLines = buildSpecMarkLine?.();
  if (specLines) {
    (option.series as any[]).push({
      name: "spec-lines",
      type: "line",
      data: [],
      markLine: specLines,
      tooltip: { show: false },
      legendHoverLink: false
    });
  }
  return option;
}

/* ========== 生命周期与监听 ========== */
onMounted(async () => {
  await nextTick();
  initChart();
  await load();
  window.addEventListener("resize", resize);
});

watch(
  () => props.active,
  async now => {
    if (now) {
      await nextTick();
      await load();
      resize();
    }
  }
);

watch(
  () => props.testName,
  async n => {
    if (n) {
      await nextTick();
      await load();
      resize();
    }
  },
  { immediate: true }
);

watch(
  () => [
    props.mode,
    props.binCount,
    props.stacked,
    props.globalBins,
    props.showFit,
    props.showSpecs
  ],
  () => {
    if (!chart) return;
    chart.setOption(buildOption(), true);
  },
  { deep: true }
);

onBeforeUnmount(() => {
  window.removeEventListener("resize", resize);
  disposeChart();
});
</script>
